package es.ise.core.less;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.asual.lesscss.LessEngine;

public class LessCompiler
{
	private static Logger logger = LoggerFactory.getLogger(LessCompiler.class);
	
	private static final FilenameFilter lessFilter = new FilenameFilter()
	{
		@Override
		public boolean accept(File dir, String name)
		{
			String parts[] = name.split("[.]");
			return parts[parts.length-1].equalsIgnoreCase("less");
		}
	};
	
	private static final Pattern externalUrlPattern = Pattern.compile("url\\((\"?http://.*?\"?)\\)");
	
	public static void main(String[] args)
	{
		if(args.length != 2)
			throw new IllegalArgumentException("Uso: java LessCompiler <less_dir> <css_dir>");
		
		File lessDir = new File(args[0]);
		if(!lessDir.exists() || !lessDir.isDirectory())
			throw new IllegalArgumentException(String.format("El directorio de fuentes LESS no existe o no es un directorio: %s", lessDir.getAbsolutePath()));
		
		File lessStylesDir = new File(lessDir, "styles");
		
		File cssDir = new File(args[1]);
		if(!cssDir.isDirectory())
			throw new IllegalArgumentException(String.format("El directorio de destino no es un directorio válido: %s", cssDir.getAbsolutePath()));
		
		LessEngine lessc = new LessEngine();
		for(File lessFile: lessStylesDir.listFiles(lessFilter))
		{
			try
			{
				String less = lessc.compile(lessFile);
				
				Set<String> urls = getExternalsUrls(less);
				Map<String, String> urlMapper = loadUrlMapper(lessDir);
				for(String url: urls)
				{
					if(!urlMapper.containsKey(url))
					{
						String image = getImage(url.replaceAll("[#\"]", ""), urlMapper, lessDir);
						urlMapper.put(url, image);
					}
					less = less.replace(url, urlMapper.get(url));
					copyFiles(new File(lessDir,"cache/"+urlMapper.get(url)), new File(cssDir, urlMapper.get(url)));
				}
				saveUrlMapper(urlMapper, lessDir);
				
				if(!cssDir.exists())
					cssDir.mkdirs();
				File cssFile = new File(cssDir, lessFile.getName().replace(".less", ".css"));
				PrintWriter out = new PrintWriter(cssFile);
				out.println(less);
				out.close();
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}
	}
	
	private static Set<String> getExternalsUrls(String less)
	{
		Set<String> urls = new HashSet<String>();
		Matcher matcher = externalUrlPattern.matcher(less);
		while(matcher.find())
		{
			urls.add(matcher.group(1));
		}
		return urls;
	}
	
	private static String getImage(String externalUrl, Map<String, String> map, File lessDir) throws IOException
	{
		logger.info(String.format("Fetching image from %s...", externalUrl));
		//Creamos un HTTPConnection mediante un objeto URL
		URL url = new URL(externalUrl);
		HttpURLConnection conn = (HttpURLConnection)url.openConnection();
		if(conn.getResponseCode()==HttpURLConnection.HTTP_OK)
		{
			String image = String.format("images/less_%03d_%d.png", map.size()+1, externalUrl.hashCode());
			File imageFile = new File(lessDir, "cache/"+image);
			if(!imageFile.getParentFile().exists())
				imageFile.getParentFile().mkdirs();
			BufferedInputStream bin = new BufferedInputStream(conn.getInputStream());
			FileOutputStream out = new FileOutputStream(imageFile);
			int b = 0;
			while((b = bin.read()) >= 0)
			{
				out.write(b);
			}
			out.close();
			bin.close();		
			return image;
		}
		else
		{
			return "";
		}
	}

	@SuppressWarnings("unchecked")
	private static Map<String, String> loadUrlMapper(File lessDir)
	{
		Map<String, String> map = new HashMap<String, String>();
		File cache = new File(lessDir, "cache/image.cache");
		if(cache.exists())
		{
			try
			{
				ObjectInputStream in = new ObjectInputStream(new FileInputStream(cache));
				map = (Map<String, String>)in.readObject();
				in.close();
			}
			catch (Exception e)
			{
				
			}
		}
		return map;
	}
	
	private static void saveUrlMapper(Map<String, String> map, File lessDir)
	{
		File cache = new File(lessDir, "cache/image.cache");
		if(!cache.getParentFile().exists())
			cache.getParentFile().mkdirs();
		try
		{
			ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(cache));
			out.writeObject(map);
			out.close();
		}
		catch (Exception e)
		{
			
		}
	}
	
	private static void copyFiles(File from, File to) throws IOException
	{
		if(!to.getParentFile().exists())
			to.getParentFile().mkdir();
		
		BufferedInputStream bin = new BufferedInputStream(new FileInputStream(from));
		FileOutputStream out = new FileOutputStream(to);
		int b = 0;
		while((b = bin.read()) >= 0)
		{
			out.write(b);
		}
		out.close();
		bin.close();		
	}
	
}
